home *** CD-ROM | disk | FTP | other *** search
Text File | 1998-10-26 | 40.0 KB | 1,167 lines | [TEXT/CWIE] |
- // OpenWith CMPlugin
- //
- // Copyright Peter O'Gorman 1998
- //
- // Parts of this file were taken directly from or are descended from other sources.
- // This file is based on Arno's Sample Plugin and ContextTypeCMPlugin (Sample)
- // from the CMM 1.02 SDK.
- // Some parts of this file are modified from MoreFiles by Jim Luther.
- // GetFinderProcess was taken unchanged from the MacHack "Finder Flocks" by Christopher Evans
- // The source file was ScriptableFinder.c by Leonard Rosenthol.
-
- // Thank you everyone for the free sources, and examples.
-
- // Class Header
- #include "OpenwithCMPlugin.h"
-
- // Mac OS Includes
- #include <CodeFragments.h>
- #include <ContextualMenuPlugins.h>
- #include <Folders.h>
- #include <Aliases.h>
- #include <Resources.h>
- #include <FinderRegistry.h>
- #include <TextUtils.h>
- #include <StringCompare.h>
-
- // SOM Includes
- #include <som.xh>
-
-
-
-
- struct BeenThere { /* To check if we have looked at a directory before */
- SInt16 vRefNum;
- SInt32 dirID;
- };
- struct IterateGlobals /* If you are going to include Morefiles then you need to change the name of this */
- {
- UInt16 numItems;
- FSSpec** applicationsHdl;
- BeenThere** ER;
- FSSpec tempSpec;
- AERecord theSuperCommand;
- Boolean wasAliased;
- Boolean targetIsFolder;
- FolderType theType;
- SInt32 tempDirID;
- CInfoPBRec cPB; /* the parameter block used for PBGetCatInfo calls */
- Str63 itemName; /* the name of the current item */
- OSErr result; /* temporary holder of results - saves 2 bytes of stack each level */
- UInt16 maxLevels; /* Maximum levels to iterate through */
- UInt16 currentLevel; /* The current level IterateLevel is on */
- };
- //defines
- #define dErr theGlobals->result // saves me typing
- #define kHandleInc 10 // the increment for our FSSpec Handle 10 * sizeof(FSSpec)
- #define kStringList 982 // STR# resource ID;
- #define iPrefFileName 1
- #define iMakeFavourites 2
- #define iOpenWith 3
-
- // Function declarations
- extern pascal OSErr __initialize(CFragInitBlockPtr); // metrowerks's default initializer
- extern pascal void __terminate(void);
- pascal OSErr OpenwithCMPluginInitialize(CFragInitBlockPtr init); // our initializer
- pascal void OpenwithCMPluginTerminate(void); // our terminator
-
- OSStatus AddCommandToAEDescList(ConstStr255Param inCommandString,
- SInt32 inCommandID, AEDescList* ioCommandList);
-
- OSType GetFileListStatus( AEDescList* fileList,
- Boolean& haveFolders);
-
- void ProcessSelection( AEDescList* fileList,SInt32 CommandID,
- FSSpec** applications,ProcessSerialNumber finderProcess);
-
- pascal OSErr DoFavourites(FSSpec * inFolder);
- pascal OSErr OpenPrefFile(Boolean inCreate,FSSpec * outSpec);
- void IterateFavourites(AEDescList* ioCommands,
- AEDesc * inContextDescriptor,
- FSSpec *** theHandle);
-
-
- Boolean BeenHereBefore(IterateGlobals *theGlobals,SInt16 vRefNum,SInt32 dirID);
-
- static void MyIterateDirectoryLevel(SInt16 vRefNum,SInt32 dirID,
- IterateGlobals *theGlobals,AEDescList * ioCommands);
-
- OSErr GetFinderProcess (ProcessSerialNumber *finderpsn, Boolean shortcut);
-
- OSStatus HaveThisAPPL(FSSpec theSpec,IterateGlobals *theGlobals,AEDescList * ioCommands);
- inline OSStatus GetIthSpec(AEDescList * inList,FSSpec * outSpec,UInt32 i);
-
- /* these cause linkage warnings with Universal headers 3.2, could rename them, I suppose */
- pascal OSErr IsAliasFile(const FSSpec *fileFSSpec,
- Boolean *aliasFileFlag,
- Boolean *folderFlag);
- pascal OSErr ResolveAliasFileWithMountFlags (FSSpec * theSpec,
- Boolean resolveAliasChains,
- Boolean * targetIsFolder,
- Boolean * wasAliased,
- unsigned long mountFlags) ;
- pascal OSErr ResolveAliasWithMountFlags (const FSSpec * fromFile,
- AliasHandle alias,
- FSSpec * target,
- Boolean * wasChanged,
- unsigned long mountFlags) ;
- // Globals -aargh - Should have made the whole thing C++ and used member variables
- SInt16 gPrefRefNum = -1;
- SInt16 gPlugRefNum = -1;
- FSSpec gMySpec;
-
- /*******************************************************************************
-
- OpenwithCMPluginInitialize
-
- All plugin SOM object must somehow register themselves so clients
- can instantiate them by name. The best place that I've found to
- do it is in the fragment initializer.
-
- *******************************************************************************/
-
- pascal OSErr OpenwithCMPluginInitialize(CFragInitBlockPtr init)
- {
- #pragma unused (init)
-
- OSErr theError = __initialize(init);
- if (theError == noErr)
- {
- // register our class with SOM
- somNewClass(OpenwithCMPlugin);
- }
- return theError;
- } // OpenwithCMPluginInitialize
-
-
-
- /*******************************************************************************
-
- OpenwithCMPlugin::Initialize
-
- *******************************************************************************/
-
- OSStatus OpenwithCMPlugin::Initialize(
- Environment*,
- FSSpec* inFileSpec)
- {
- // Store Pligins FSSpec;
- BlockMoveData((Ptr)inFileSpec,&gMySpec,sizeof(FSSpec));
-
- return noErr;
-
- } // OpenwithCMPlugin::Initialize
-
-
-
- /*******************************************************************************
-
- OpenwithCMPlugin::ExamineContext
-
- *******************************************************************************/
-
- OSStatus OpenwithCMPlugin::ExamineContext(
- Environment*,
- AEDesc *inContextDescriptor,
- SInt32 inTimeOutInTicks,
- AEDescList* ioCommands,
- Boolean* outNeedMoreTime)
- {
- #pragma unused(inTimeOutInTicks)
- if (inContextDescriptor != NULL)
- {
-
- OSErr theErr = GetFinderProcess (&finderProcess, false);
- if (theErr == noErr){
- // If the finder is running, we can go on.
- gPlugRefNum = ::FSpOpenResFile(&gMySpec,fsCurPerm);
- if (gPlugRefNum != -1){
- IterateFavourites(ioCommands,inContextDescriptor,&mFileHandle);
- }
- }
-
-
- }
-
- *outNeedMoreTime = false;
- return noErr;
-
- } // OpenwithCMPlugin::ExamineContext
-
-
-
- /*******************************************************************************
-
- OpenwithCMPlugin::HandleSelection
-
- *******************************************************************************/
-
- OSStatus OpenwithCMPlugin::HandleSelection(
- Environment*,
- AEDesc *inContextDescriptor,
- SInt32 inCommandID)
- {
-
- ProcessSelection(inContextDescriptor,inCommandID,mFileHandle,finderProcess);
-
- return noErr;
-
- } // OpenwithCMPlugin::HandleSelection
-
-
-
- /*******************************************************************************
-
- OpenwithCMPlugin::PostMenuCleanup
-
- *******************************************************************************/
-
- OSStatus OpenwithCMPlugin::PostMenuCleanup(
- Environment*)
- {
- // Dispose of things
- ::DisposeHandle((Handle)mFileHandle);
- if (gPrefRefNum != -1){
- CloseResFile(gPrefRefNum);
- gPrefRefNum = -1;
- }
- // Could/should keep the plugin res file open all the time?
- if (gPlugRefNum != -1){
- CloseResFile(gPlugRefNum);
- gPlugRefNum = -1;
- }
- return noErr;
-
- } // OpenwithCMPlugin::PostMenuCleanup
-
-
-
- /*******************************************************************************
-
- AddCommandToAEDescList from Apple sample code
-
- *******************************************************************************/
-
- OSStatus AddCommandToAEDescList(
- ConstStr255Param inCommandString,
- SInt32 inCommandID,
- AEDescList* ioCommandList)
- {
- OSStatus theError = noErr;
-
- AERecord theCommandRecord = { typeNull, NULL };
-
- do
- {
- // create an apple event record for our command
- theError = ::AECreateList(NULL, 0, true, &theCommandRecord);
- if (theError != noErr) break;
-
- // stick the command text into the aerecord
- theError = ::AEPutKeyPtr(&theCommandRecord, keyAEName, typeChar,
- &inCommandString[1], inCommandString[0]);
- if (theError != noErr) break;
-
- // stick the command ID into the AERecord
- theError = ::AEPutKeyPtr(&theCommandRecord, keyContextualMenuCommandID, typeLongInteger,
- &inCommandID, sizeof (inCommandID));
- if (theError != noErr) break;
-
- // stick this record into the list of commands that we are passing back to CMM
- theError = ::AEPutDesc(ioCommandList, // the list we're putting our command into
- 0, // stick this command onto the end of our list
- &theCommandRecord); // the command I'm putting into the list
-
- } while (false);
- // clean up after ourself; dispose of the AERecord
- AEDisposeDesc(&theCommandRecord);
-
- return theError;
-
- } // AddCommandToAEDescList
-
- // -----------------------------------------------------------------------------------
- // OSStatus GetIthSpec(AEDescList * inList,FSSpec * outSpec,SInt32 i)
- // I was doing this in a few places, so made a function of it.
- // Input Apple Descriptor List
- // Input i - the number
- // Output the Spec (must be allocated)
- // -----------------------------------------------------------------------------------
- inline OSStatus GetIthSpec(AEDescList * inList,FSSpec * outSpec,UInt32 i)
- {
-
- OSErr err = noErr;
- AEDesc file = {typeNull, NULL};
- AEKeyword theKeyword;
- AEDesc coercedFile = {typeNull, NULL};
- if (outSpec == NULL) return paramErr; // sanity
- do {
- err = ::AEGetNthDesc(inList, i, typeWildCard, &theKeyword, &file);
- if (err != noErr) break;
- // Try to get an FSSpec out of the item in the descriptor; make sure to
- // coerce it, cuz the app may have passed an object specifier.
- AEDesc coercedFile = {typeNull, NULL};
- err = ::AECoerceDesc(&file, typeFSS, &coercedFile);
- if (err != noErr) break;
- if (coercedFile.descriptorType == typeNull){
- err = fnfErr;
- break;
- }
- // Pull the FSSpec out of the descriptor
- ::BlockMoveData(*coercedFile.dataHandle, outSpec,
- ::GetHandleSize(coercedFile.dataHandle));
- }while(false);
-
- // Disposed of the AEDesc
- AEDisposeDesc(&file);
- AEDisposeDesc(&coercedFile);
- return err;
- }
-
- // ---------------------------------------------------------------------------
- // GetFileListStatus // modified from Arno's Sample Plugin that came with CMM SDK
- // ---------------------------------------------------------------------------
- // Return if all the files are expandable or compressable.
- // Return the number of files in the selection
-
- OSType
- GetFileListStatus( AEDescList* fileList,
- Boolean& haveFolders)
- {
- OSErr err;
- OSType firstCreator = 0;
- haveFolders = false;
- Boolean itisMe = false;
- Boolean HaveValidFiles = false;
- Boolean HaveUnresolvedAliases = false;
- Boolean targetIsFolder,wasAliased;
- SInt32 listItemsCount = 0;
- FSSpec fileSpec;
- if ( AECountItems(fileList, &listItemsCount)== noErr)
- {
- for (UInt32 i = 1; i <= listItemsCount; i++)
- {
- err = GetIthSpec(fileList,&fileSpec,i);
- if (err != noErr) break;
- // Get info about the file
- CInfoPBRec fileInfo;
- fileInfo.hFileInfo.ioCompletion = NULL; // synchronous
- fileInfo.hFileInfo.ioNamePtr = fileSpec.name;
- fileInfo.hFileInfo.ioVRefNum = fileSpec.vRefNum;
- fileInfo.hFileInfo.ioFDirIndex = 0; // search for the named item
- fileInfo.hFileInfo.ioDirID = fileSpec.parID;
- err = ::PBGetCatInfoSync(&fileInfo);
- if (err != noErr) break;
- /* Is it An Alias */
- if ( (fileInfo.hFileInfo.ioFlAttrib & ioDirMask) == 0 ){
- if ( (fileInfo.hFileInfo.ioFlFndrInfo.fdFlags & 0x8000) != 0){
- err = ResolveAliasFileWithMountFlags(&fileSpec,
- true,
- &targetIsFolder,
- &wasAliased,
- kResolveAliasFileNoUI);
- if (err != noErr) {
- HaveUnresolvedAliases = true;
- err = noErr;
- continue;
- }
- fileInfo.dirInfo.ioFDirIndex = 0;
- fileInfo.hFileInfo.ioVRefNum = fileSpec.vRefNum;
- fileInfo.dirInfo.ioDrDirID = fileSpec.parID;
- fileInfo.hFileInfo.ioNamePtr = (StringPtr)&fileSpec.name;
- err = PBGetCatInfoSync((CInfoPBPtr)&fileInfo);
- if (err != noErr){
- break;
- }
- }
- }
- // see if we have a folder
- if ((fileInfo.hFileInfo.ioFlAttrib & ioDirMask) != 0)
- {
- FolderType theType = nil;
- err = IdentifyFolder( fileInfo.hFileInfo.ioVRefNum,
- fileInfo.hFileInfo.ioDirID,
- &theType);
- if (err != noErr){
- haveFolders = true;
- err = noErr;
- }
- else if ((theType != kTrashFolderType) &&
- (theType != kFontsFolderType) &&
- (theType != kExtensionFolderType)) // we could go wild here...
- haveFolders = true;
- else {
- firstCreator = 'Err ';
- }
- }
- else if ((fileInfo.hFileInfo.ioFlAttrib & ioDirMask) == 0)
- {
- if ((fileInfo.hFileInfo.ioFlFndrInfo.fdType == 'cmnu') &&
- (fileSpec.vRefNum == gMySpec.vRefNum) &&
- (fileSpec.parID == gMySpec.parID) &&
- (EqualString(fileSpec.name,gMySpec.name,false,false)))
- itisMe = true;
- else {
- HaveValidFiles = true;
- }
- }
-
- }
- }
- if (haveFolders && (listItemsCount == 1)) {
- firstCreator = 'fold';
- }else
- if ((!haveFolders) && (itisMe) && (listItemsCount == 1)) firstCreator = ' ME ';
- else
- if ((!haveFolders) && (!itisMe) && (HaveUnresolvedAliases) && (!HaveValidFiles)) firstCreator = 'Err ';
- if (err != noErr) firstCreator = 'Err ';
- return firstCreator;
- }
- // ---------------------------------------------------------------------------
- // void ProcessSelection(AEDescList* fileList,
- // SInt32 CommandID,
- // FSSpec** applications,
- // ProcessSerialNumber finderProcess)
- // ---------------------------------------------------------------------------
- // Handle User Choice;
- // Inputs - AEDescList* fileList - the file selection
- // - SInt32 CommandID - either -32000 MAke Favourites folder of the FSSpec index
- // - FSSpec** applications - Our FSSpec handle
- // - the finder PSN
- // Returns void
-
- void ProcessSelection(AEDescList* fileList,SInt32 CommandID,FSSpec** applications,ProcessSerialNumber finderProcess)
- {
- OSErr err;
- OSType firstCreator = nil;
- SInt32 listItemsCount = 0;
- AEDesc file = {typeNull, NULL};
- FSSpec fileSpec;
-
- if (CommandID == -32000){ // Set Favourite folder
- AliasHandle alisHandle;
- Boolean wasChanged;
- OSErr theErr;
- Boolean wasAliased, targetIsFolder;
- theErr = OpenPrefFile(true,NULL);
- UseResFile(gPrefRefNum);
- alisHandle = (AliasHandle)::Get1IndResource(rAliasType, 1);
- theErr = ResError();
- theErr = GetIthSpec(fileList,&fileSpec,1);
- if (theErr == noErr) {
- err = ResolveAliasFileWithMountFlags(&fileSpec,
- true,
- &targetIsFolder,
- &wasAliased,
- kResolveAliasFileNoUI);
- if (alisHandle == nil){
- theErr = ::NewAlias(nil,&fileSpec,&alisHandle);
- if (theErr == noErr){
- AddResource((Handle)alisHandle, rAliasType, 0, fileSpec.name);
- WriteResource((Handle)alisHandle);
- theErr = ResError();
- }
- }
- else {
- theErr = UpdateAlias(nil,&fileSpec,alisHandle,&wasChanged);
- }
- ChangedResource((Handle)alisHandle);
- WriteResource((Handle)alisHandle);
- }
- return;
- } // End Set favourite folder - should have been a function
-
- if (AECountItems(fileList, &listItemsCount) == noErr)
- {
- AppleEvent theOpenEvent,theReply;
- AEDesc theFinder = {typeNull,NULL};
- AEDesc theApplication = {typeNull,NULL};
- AEDescList theDocuments = {typeNull,NULL};
- AliasHandle aDocument;
- err = ::AECreateList(nil,0,false,&theDocuments);
- if (err != noErr) return;
-
- for (UInt32 i = 1; i <= listItemsCount; i++)
- {
-
- err = GetIthSpec(fileList, &fileSpec,i);
- if (err != noErr) break;
- err = NewAliasMinimal(&fileSpec, &aDocument);
- if (err == noErr){
- HLock((Handle)aDocument);
- err = AEPutPtr(&theDocuments, 0, typeAlias, *aDocument, GetHandleSize((Handle)aDocument));
- DisposeHandle((Handle)aDocument);
- }
- }
-
- // Now We have the documents in a list we know is acceptable
- // probably didn't need the above, as we could have used the list we were passed
- // but..
- do {
- if (CommandID < 0) break; // am I sane?
- err = AECreateDesc(typeProcessSerialNumber, &finderProcess,
- sizeof(finderProcess), &theFinder);
- if (err != noErr) break;
- err = AECreateAppleEvent(kCoreEventClass, kAEOpenDocuments,
- &theFinder, kAutoGenerateReturnID,
- kAnyTransactionID, &theOpenEvent);
- if (err != noErr) break;
- err = AEPutParamDesc(&theOpenEvent, keyDirectObject, &theDocuments);
- if (err != noErr) break;
- HLock((Handle)applications);
- err = NewAlias(nil,&(*applications)[CommandID],&aDocument);
- HUnlock((Handle)applications);
- if (err != noErr) break;
- HLock((Handle)aDocument);
- err = AECreateDesc(typeAlias, *aDocument,
- ::GetHandleSize((Handle)aDocument), &theApplication);
- DisposeHandle((Handle)aDocument);
- if (err != noErr) break;
- err = AEPutParamDesc(&theOpenEvent, keyAEUsing, &theApplication);
-
- err = AESend(&theOpenEvent, &theReply, kAENoReply, kAENormalPriority,
- kAEDefaultTimeout, nil, nil);
-
- } while (false);
-
- AEDisposeDesc(&theFinder);
- AEDisposeDesc(&theApplication);
- AEDisposeDesc(&theDocuments);
- AEDisposeDesc(&theOpenEvent);
- AEDisposeDesc(&theReply);
-
- }
-
- return;
- }
-
- // -----------------------------------------------------------
- // pascal OSErr OpenPrefFile(Boolean inCreate, FSSpec * PrefSpec)
- // -----------------------------------------------------------
- // Opens our preferences file
- // Input inCreate - if true creates a preffile if there wasn't one.
- // Output PrefSpec - Our preffiles FSSpec.
- pascal OSErr OpenPrefFile(Boolean inCreate, FSSpec * PrefSpec)
- {
- OSErr theErr = noErr;
- SInt16 vRefNum;
- SInt32 dirID;
- Str255 myFileName;
- FSSpec myPrefFile;
- if (gPrefRefNum != -1) return noErr;
- do {
-
- GetIndString(myFileName,kStringList,iPrefFileName);
- theErr = FindFolder(kOnSystemDisk,kPreferencesFolderType,kDontCreateFolder,&vRefNum,&dirID);
- if (theErr != noErr) break;
- theErr = FSMakeFSSpec(vRefNum,dirID,myFileName,&myPrefFile);
- if (PrefSpec != NULL) BlockMoveData((Ptr)&myPrefFile,PrefSpec,sizeof(FSSpec));
- if ((theErr != noErr) && (theErr != fnfErr)) break;
- if ((inCreate) && (theErr == fnfErr)){
- FSpCreateResFile(&myPrefFile, '????', 'pref', smSystemScript);
- theErr = ResError();
- }
- if (theErr != noErr) break;
- gPrefRefNum = FSpOpenResFile(&myPrefFile, fsRdWrPerm);
- theErr = ResError();
- } while (false);
- return theErr;
- }
-
-
- // -----------------------------------------------------------
- // pascal OSErr DoFavourites(FSSpec * inFolder)
- // -----------------------------------------------------------
- // REsolves the alias for the favourites folder returns the resolved FSSpec
- pascal OSErr DoFavourites(FSSpec * inFolder)
- {
-
- OSErr theErr;
- Boolean targetIsFolder;
- Boolean wasChanged;
- Handle alisHandle = nil;
- do {
- OpenPrefFile(false,inFolder);
- if (gPrefRefNum == -1) break;
- UseResFile(gPrefRefNum);
- /* the first 'alis' resource in the file is the appropriate alias */
- alisHandle = Get1IndResource(rAliasType, 1);
- theErr = ResError();
- if (alisHandle == nil) break;
- /* load the resource explicitly in case SetResLoad(FALSE) */
- LoadResource(alisHandle);
- theErr = ResError();
- if (theErr != noErr) break;
- theErr = ResolveAliasWithMountFlags( nil,
- (AliasHandle)alisHandle,
- inFolder,
- &wasChanged,
- kResolveAliasFileNoUI);
- theErr = IsAliasFile(inFolder,
- &wasChanged,
- &targetIsFolder);
- } while (false) ;
- if ((theErr == noErr) && targetIsFolder){
- return noErr;
- }
-
- return fnfErr;
- }
-
- // -----------------------------------------------------------
- // void IterateFavourites(AEDescList* ioCommands,AEDesc * inContextDescriptor,FSSpec *** theHandle)
- // -----------------------------------------------------------
- // Adds the commands
- // Returns an FSSpec handle with the found applications
- void IterateFavourites(AEDescList* ioCommands,AEDesc * inContextDescriptor,FSSpec *** theHandle)
- {
- // Should rewrite the first part of this.
- IterateGlobals theGlobals;
- OSErr result = noErr;
- SInt32 theDirID;
- SInt16 theVRefNum;
- FSSpec inFolder;
- SInt16 numItems = 0;
- //
- Boolean haveFolders;
- OSType theCreator = GetFileListStatus( inContextDescriptor, haveFolders);
- if (theCreator == 'Err '){
- return;
- }
- theGlobals.numItems = 0;
- theGlobals.applicationsHdl = (FSSpec**)TempNewHandle(sizeof(FSSpec)*kHandleInc,&result);
- if ((theGlobals.applicationsHdl == nil) || (result != noErr)) return;
- theGlobals.result = noErr; /* temporary holder of results - saves 2 bytes of stack each level */
- AEDescList mySubMenu = {typeNull,NULL};
- result = AECreateList(NULL,0,false,&mySubMenu);
- if (result == noErr){
- if (theCreator == 'fold') {
- // Updated 10/20/98 Now checks if the directory if already the favourite
- FSSpec folderSpec;
- FSSpec inFolderSpec;
- result = DoFavourites(&folderSpec);
- if (result == noErr){
- result = GetIthSpec(inContextDescriptor,&inFolderSpec,1);
- if (result == noErr){
- if ((folderSpec.vRefNum != inFolderSpec.vRefNum) ||
- (folderSpec.parID != inFolderSpec.parID) ||
- !(EqualString(folderSpec.name,inFolderSpec.name,false,false))){
- Str255 aString;
- GetIndString(aString,kStringList,iMakeFavourites);
- AddCommandToAEDescList(aString,-32000,ioCommands);
- }
- }
- }
- }
- else if (!haveFolders){
-
-
- result = DoFavourites(&inFolder);
- if (result == noErr){
- theGlobals.cPB.dirInfo.ioFDirIndex = 0;
- theGlobals.cPB.hFileInfo.ioVRefNum = inFolder.vRefNum;
- theGlobals.cPB.dirInfo.ioDrDirID = inFolder.parID;
- theGlobals.cPB.hFileInfo.ioNamePtr = (StringPtr)&inFolder.name;
- result = PBGetCatInfoSync((CInfoPBPtr)&theGlobals.cPB);
- if ( result == noErr )
- {
-
- if ( (theGlobals.cPB.hFileInfo.ioFlAttrib & ioDirMask) != 0 )
- {
- theDirID = theGlobals.cPB.dirInfo.ioDrDirID;
- theVRefNum = theGlobals.cPB.hFileInfo.ioVRefNum;
- /* Set up the globals we need to access from the recursive routine. */
- theGlobals.cPB.hFileInfo.ioNamePtr = (StringPtr)&theGlobals.itemName;
- theGlobals.cPB.hFileInfo.ioVRefNum = theVRefNum;
- theGlobals.itemName[0] = 0;
- theGlobals.result = noErr;
- theGlobals.theSuperCommand.descriptorType = typeNull;
- theGlobals.theSuperCommand.dataHandle = NULL;
- theGlobals.maxLevels = 4; /* seems to be like a 'Magic Number' */
- theGlobals.currentLevel = 0; /* start at level 0 */
- theGlobals.ER = (BeenThere**)TempNewHandle(0,&result);
- if ((theGlobals.ER != nil) && (result == noErr)){
- /* Here we go into recursion land... */
- MyIterateDirectoryLevel(theVRefNum,theDirID, &theGlobals,&mySubMenu);
- ::DisposeHandle((Handle)theGlobals.ER);
- result = theGlobals.result; /* set the result */
- }
- }
-
- else
- {
- result = dirNFErr; /* a file was passed instead of a directory */
- }
- }
-
- }
- }
- if (AECountItems(&mySubMenu,&theGlobals.tempDirID) == noErr){
-
- if (theGlobals.tempDirID > 0){
- result = ::AECreateList(NULL,0,true,&theGlobals.theSuperCommand);
- if (result == noErr){
- Str255 theSupercommandText;
- GetIndString(theSupercommandText,kStringList,iOpenWith);
- result = ::AEPutKeyPtr(&theGlobals.theSuperCommand, keyAEName, typeChar,
- &theSupercommandText[1], theSupercommandText[0]);
- if (result == noErr){
- result = ::AEPutKeyDesc(&theGlobals.theSuperCommand, keyContextualMenuSubmenu,
- &mySubMenu);
- if (result == noErr){
- result = ::AEPutDesc(ioCommands, // the list we're putting our command into
- 0, // stick this command onto the end of our list
- &theGlobals.theSuperCommand); // the command I'm putting into the list
- }
- }
- AEDisposeDesc(&theGlobals.theSuperCommand);
- }
- }
-
- }
- AEDisposeDesc(&mySubMenu);
- }
- *theHandle = theGlobals.applicationsHdl;
- return ;
- }
-
-
-
-
-
-
-
- //*****************************************************************************
- // static void MyIterateDirectoryLevel(SInt16 vRefNum,SInt32 dirID,
- // IterateGlobals *theGlobals,AEDescList * ioCommands)
- //
- //
- //*****************************************************************************
- //
- // The main routine, taken from Morefiles IterateDirectory.c and modified to work with aliases
- // Most of the bugs were here, hopefully killed most of them.
- static void MyIterateDirectoryLevel(SInt16 vRefNum,SInt32 dirID,
- IterateGlobals *theGlobals,AEDescList * ioCommands)
- {
-
-
- if ( (theGlobals->maxLevels == 0) || /* if maxLevels is zero, we aren't checking levels */
- (theGlobals->currentLevel < theGlobals->maxLevels) ) /* if currentLevel < maxLevels, look at this level */
- {
- if (!BeenHereBefore(theGlobals,vRefNum,dirID)){
- SInt16 index = 1;
- AEDescList ioSubMenuCommands = {typeNull,NULL}; // adds to stack space but can't avoid it.
- SInt16 saveVref;
- SInt32 saveDirID;
- ++theGlobals->currentLevel; /* go to next level */
-
- do
- { /* Isn't C great... What I'd give for a "WITH theGlobals DO" about now... */
-
- /* Get next source item at the current directory level */
- theGlobals->itemName[0] = 0;
- theGlobals->cPB.hFileInfo.ioNamePtr = (StringPtr)&theGlobals->itemName;
- theGlobals->cPB.dirInfo.ioFDirIndex = index;
- theGlobals->cPB.hFileInfo.ioVRefNum = vRefNum;
- theGlobals->cPB.dirInfo.ioDrDirID = dirID;
-
- dErr = PBGetCatInfoSync((CInfoPBPtr)&theGlobals->cPB);
- if (dErr != noErr) break;
-
- /* Is it An Alias */
- if ( (theGlobals->cPB.hFileInfo.ioFlAttrib & ioDirMask) == 0 ){
- if ( (theGlobals->cPB.hFileInfo.ioFlFndrInfo.fdFlags & 0x8000) != 0){
-
- dErr=::FSMakeFSSpec(vRefNum,
- dirID,
- theGlobals->cPB.hFileInfo.ioNamePtr,
- &theGlobals->tempSpec);
- if (dErr != noErr) break; // shouldn't happen
- dErr = ResolveAliasFileWithMountFlags(&theGlobals->tempSpec,
- true,
- &theGlobals->targetIsFolder,
- &theGlobals->wasAliased,
- kResolveAliasFileNoUI);
- if (dErr != noErr) { // If the alias can't be resolved, it isn't
- dErr = noErr; // a problem.
- index++; // <- this is very important
- continue;
- }
- theGlobals->cPB.dirInfo.ioFDirIndex = 0;
- theGlobals->cPB.hFileInfo.ioVRefNum = theGlobals->tempSpec.vRefNum;
- theGlobals->cPB.dirInfo.ioDrDirID = theGlobals->tempSpec.parID;
- theGlobals->cPB.hFileInfo.ioNamePtr = (StringPtr)&theGlobals->tempSpec.name;
- dErr = PBGetCatInfoSync((CInfoPBPtr)&theGlobals->cPB);
- if (dErr != noErr) break; // no Big deal.
-
- }
- }
- dErr=::FSMakeFSSpec(theGlobals->cPB.hFileInfo.ioVRefNum,
- theGlobals->cPB.dirInfo.ioDrParID,
- theGlobals->cPB.hFileInfo.ioNamePtr,
- &theGlobals->tempSpec);
- if (dErr != noErr) break;
- // Did we find an Application?
- // I decided not to check appe or APPC, as they usually can't open much
- if ( (theGlobals->cPB.hFileInfo.ioFlAttrib & ioDirMask) == 0 ){
- if (theGlobals->cPB.hFileInfo.ioFlFndrInfo.fdType == 'APPL'){
- dErr = HaveThisAPPL(theGlobals->tempSpec,theGlobals,ioCommands);
- if (dErr != noErr) break; // probably won't happen, but would be memFullErr
-
- }
- }
- else { // it is a directory
- dErr = ::AECreateList(NULL,0,false,&ioSubMenuCommands);
- if (dErr != noErr) break;
- saveVref = theGlobals->cPB.hFileInfo.ioVRefNum;
- saveDirID = theGlobals->cPB.dirInfo.ioDrDirID;
- dErr = IdentifyFolder( saveVref,
- saveDirID,
- &theGlobals->theType);
- if (dErr != noErr){
- dErr = noErr;
- }
- else if ((theGlobals->theType == kTrashFolderType ) || // skip these types
- ( theGlobals->theType == kFontsFolderType)){
- index++;
- continue;
- }
- // Recursion here
- MyIterateDirectoryLevel(saveVref,saveDirID, theGlobals,&ioSubMenuCommands);
- if (dErr != noErr) break;
-
- dErr = ::AECountItems(&ioSubMenuCommands,&theGlobals->tempDirID); // it is an SInt32.
- if (dErr != noErr) break;
- if ( theGlobals->tempDirID > 0 ){ // some items are in the list
- // Saves stack space by not having to store the name.
- theGlobals->itemName[0] = 0;
- theGlobals->cPB.hFileInfo.ioNamePtr = (StringPtr)&theGlobals->itemName;
- theGlobals->cPB.dirInfo.ioFDirIndex = index;
- theGlobals->cPB.hFileInfo.ioVRefNum = vRefNum;
- theGlobals->cPB.dirInfo.ioDrDirID = dirID;
- dErr = PBGetCatInfoSync((CInfoPBPtr)&theGlobals->cPB);
- if (dErr != noErr) break;
- // Create the SubMenu Holder, if the number of items in the sub menu is more than zero
- dErr = ::AECreateList(NULL,0,true,&theGlobals->theSuperCommand);
- if (dErr != noErr) break;
- dErr = ::AEPutKeyPtr(&theGlobals->theSuperCommand,
- keyAEName, typeChar,
- &theGlobals->itemName[1],
- (char)theGlobals->itemName[0]);
- if (dErr != noErr) break;
- dErr = AEPutKeyDesc(&theGlobals->theSuperCommand,
- keyContextualMenuSubmenu,
- &ioSubMenuCommands);
- if (dErr != noErr) break;
- // I know I dispose of things multiple times, but I want to free up memory
- // as soon as possible.
- AEDisposeDesc(&ioSubMenuCommands);
- dErr = AEPutDesc(ioCommands,0,&theGlobals->theSuperCommand);
- if (dErr != noErr) break;
- AEDisposeDesc(&theGlobals->theSuperCommand);
- }
-
- AEDisposeDesc(&ioSubMenuCommands);
- }
- ++index; /* prepare to get next item */
- } while ( (dErr == noErr) ); /* time to fall back a level? */
- AEDisposeDesc(&theGlobals->theSuperCommand);
- AEDisposeDesc(&ioSubMenuCommands);
- if ( (dErr == fnfErr) || /* fnfErr is OK - it only means we hit the end of this level */
- (dErr == afpAccessDenied) ) /* afpAccessDenied is OK, too - it only means we cannot see inside a directory */
- {
- dErr = noErr;
- }
- --theGlobals->currentLevel; /* return to previous level as we leave */
- }
- }
- }
-
-
- /*
- Have we seen this directory before? Due to the inclusion of aliases we could get Apples
- Address - 1 Infinite loop. Therefore we must check.
- */
- Boolean
- BeenHereBefore(IterateGlobals *theGlobals,SInt16 vRefNum,SInt32 dirID)
- {
- ::HLock((Handle)theGlobals->ER);
- BeenThere * myPtr = *theGlobals->ER;
- Size lByteCnt = ::GetHandleSize((Handle)theGlobals->ER);
- UInt32 numEntries = lByteCnt / sizeof(BeenThere);
- for (UInt32 i = 0;i < numEntries; i++){
- if ((myPtr[i].vRefNum == vRefNum) && (myPtr[i].dirID == dirID)){
- ::HUnlock((Handle)theGlobals->ER);
- return true;
- }
- }
- ::HUnlock((Handle)theGlobals->ER);
- ::SetHandleSize((Handle)theGlobals->ER, lByteCnt + sizeof(BeenThere));
- if (MemError() != noErr) return true;
- ::HLock((Handle)theGlobals->ER);
- myPtr = *theGlobals->ER;
- myPtr[numEntries].vRefNum = vRefNum;
- myPtr[numEntries].dirID = dirID;
- HUnlock((Handle)theGlobals->ER);
- return false;
- }
-
-
- // -----------------------------------------------------------
- // OSErr GetFinderProcess (ProcessSerialNumber *finderpsn, Boolean shortcut)
- // -----------------------------------------------------------
- // Taken unmodified from ScriptableFinder.c
- OSErr GetFinderProcess (ProcessSerialNumber *finderpsn, Boolean shortcut)
-
- /* A routine to determine the process serial number of the Finder. It returns the result as a
- parameter passed by reference. It also has a Boolean parameter which specifies whether or not
- the returned process serial number will represent the Finder process serial number as
- "kCurrentProcess" if it is the current process to allow a shortcut of the _GetNextEvent call
- dependencies of processing an AppleEvent.
-
- Input: shortcut - allow "kCurrentProcess" to be used if we are in the current process.
- Input: *finderpsn - result ProcessSerialNumber passed by reference.
-
- Output: error code that occured. */
-
- {
- Boolean result;
- ProcessSerialNumber psn, currentpsn;
- ProcessInfoRec pir;
- #define kFinderType 'FNDR'
- #define kFinderSignature 'MACS'
- psn.highLongOfPSN = 0;
- psn.lowLongOfPSN = kNoProcess;
- pir.processInfoLength = sizeof(ProcessInfoRec);
- pir.processName = nil; // don't want these bits of information
- pir.processAppSpec = nil;
- while (GetNextProcess(&psn) == noErr)
- if (GetProcessInformation(&psn, &pir) == noErr)
- if ((pir.processType == kFinderType) && (pir.processSignature == kFinderSignature))
- {
- if (shortcut && (GetCurrentProcess(¤tpsn) == noErr) &&
- (SameProcess(¤tpsn, &psn, &result) == noErr) && result)
- { // use the current process to shortcut the event dispatching
- finderpsn->highLongOfPSN = 0;
- finderpsn->lowLongOfPSN = kCurrentProcess;
- }
- else
- *finderpsn = psn; // found the Finder's psn
- return(noErr); // got the process serial number
- }
- return(procNotFound); // got an error - not found
- }
-
- // -----------------------------------------------------------
- // OSStatus HaveThisAPPL(FSSpec theSpec,IterateGlobals *theGlobals,AEDescList * ioCommands)
- // -----------------------------------------------------------
- // Checks to see if the application is in our list
- // Adds it to the menu, only adds a Spec if it wasn't there before, but always adds the menu item.
- //
- OSStatus HaveThisAPPL(FSSpec theSpec,IterateGlobals *theGlobals,AEDescList * ioCommands)
- {
- OSErr theErr = noErr;
- UInt16 i;
- HLock((Handle)theGlobals->applicationsHdl);
- for(i = 0; i < theGlobals->numItems; i++){
- if ((*theGlobals->applicationsHdl)[i].vRefNum == theSpec.vRefNum){
- if ((*theGlobals->applicationsHdl)[i].parID == theSpec.parID) {
- if ((*theGlobals->applicationsHdl)[i].name[0] == theSpec.name[0]){
- // If it isn't exactly equal then it is different
- if (EqualString(theSpec.name, (*theGlobals->applicationsHdl)[i].name, false, false))
- break;
- }
- }
- }
- }
- HUnlock((Handle)theGlobals->applicationsHdl);
- if (GetHandleSize((Handle)theGlobals->applicationsHdl) <= (theGlobals->numItems * sizeof(FSSpec))){
- SetHandleSize((Handle)theGlobals->applicationsHdl,(theGlobals->numItems + kHandleInc)*sizeof(FSSpec));
- theErr = MemError();
- }
- if (theErr == noErr){
- HLock((Handle)theGlobals->applicationsHdl);
- if ( i == theGlobals->numItems){
- ::BlockMoveData(&theSpec,&((*theGlobals->applicationsHdl)[i]),sizeof(FSSpec));
- theGlobals->numItems++;
- }
- HUnlock((Handle)theGlobals->applicationsHdl);
- theErr = AddCommandToAEDescList( theGlobals->itemName,i,ioCommands); // the Alias files name
- }
- return theErr;
- }
-
- /*-----------------------------*
- | ResolveAliasWithMountFlags |
- *-----------------------------*/
-
-
- pascal OSErr ResolveAliasFileWithMountFlags (FSSpec * fileFSSpec,
- Boolean resolveAliasChains,
- Boolean * targetIsFolder,
- Boolean * wasAliased,
- unsigned long mountFlags)
- {
-
- /* maximum number of aliases to resolve before giving up */
- #define MAXCHAINS 10
-
- short myResRefNum;
- Handle alisHandle;
- FSSpec initFSSpec;
- Boolean updateFlag, foundFlag, wasAliasedTemp, specChangedFlag;
- short chainCount;
- OSErr retCode;
- Boolean mountRemoteVols = (mountFlags != kResolveAliasFileNoUI );
-
- if (fileFSSpec == nil || targetIsFolder == nil || wasAliased == nil)
- return paramErr;
-
- initFSSpec = *fileFSSpec; /* so FSSpec can be restored in case of error */
- chainCount = MAXCHAINS; /* circular alias chain protection */
- myResRefNum = -1; /* resource file not open */
-
- *targetIsFolder = foundFlag = specChangedFlag = false;
-
- /* loop through chain of alias files */
- do {
- chainCount--;
-
- /* check if FSSpec is an alias file or a directory */
- /* note that targetIsFolder => NOT wasAliased */
-
- retCode = IsAliasFile(fileFSSpec, wasAliased, targetIsFolder);
- if (retCode != noErr || !(*wasAliased)) break;
-
- /* get the resource file reference number */
- myResRefNum = FSpOpenResFile(fileFSSpec, fsCurPerm);
- retCode = ResError();
- if (myResRefNum == -1) break;
-
- /* the first 'alis' resource in the file is the appropriate alias */
- alisHandle = Get1IndResource(rAliasType, 1);
- retCode = ResError();
- if (alisHandle == nil) break;
-
- /* load the resource explicitly in case SetResLoad(FALSE) */
- LoadResource(alisHandle);
- retCode = ResError();
- if (retCode != noErr) break;
-
- retCode = FollowFinderAlias(fileFSSpec, (AliasHandle) alisHandle,
- mountRemoteVols, fileFSSpec, &updateFlag);
- /* FollowFinderAlias returns nsvErr if volume not mounted */
-
- if (retCode == noErr) {
-
- if (updateFlag) {
- /* the resource in the alias file needs updating */
- ChangedResource(alisHandle);
- WriteResource(alisHandle);
- }
-
- specChangedFlag = true; /* in case of error, restore file spec */
-
- retCode = IsAliasFile(fileFSSpec, &wasAliasedTemp, targetIsFolder);
- if (retCode == noErr)
- /* we're done unless it was an alias file and we're following a chain */
- foundFlag = !(wasAliasedTemp && resolveAliasChains);
-
- }
-
- CloseResFile(myResRefNum);
- myResRefNum = -1;
-
- } while (retCode == noErr && chainCount > 0 && !foundFlag);
-
- /* return file not found error for circular alias chains */
- if (chainCount == 0 && !foundFlag) retCode = fnfErr;
-
- /* if error occurred, close resource file and restore the original FSSpec */
-
- if (myResRefNum != -1) CloseResFile(myResRefNum);
- if (retCode != noErr && specChangedFlag) *fileFSSpec = initFSSpec;
-
- return retCode;
- }
-
- /*-------------*
- | IsAliasFile |
- *-------------*/
-
- pascal OSErr IsAliasFile(const FSSpec *fileFSSpec,
- Boolean *aliasFileFlag,
- Boolean *folderFlag)
- /* sets aliasFileFlag if the FSSpec points to an alias file;
- sets folderFlag if the FSSpec points to a folder */
-
- {
- CInfoPBRec myCInfoPBRec;
- OSErr retCode;
-
- if (fileFSSpec == nil || aliasFileFlag == nil || folderFlag == nil)
- return paramErr;
-
- *aliasFileFlag = *folderFlag = false;
-
- /* get the item's catalog information */
- myCInfoPBRec.hFileInfo.ioCompletion = nil;
- myCInfoPBRec.hFileInfo.ioNamePtr = (StringPtr)&fileFSSpec->name;
- myCInfoPBRec.hFileInfo.ioVRefNum = fileFSSpec->vRefNum;
- myCInfoPBRec.hFileInfo.ioDirID = fileFSSpec->parID;
- myCInfoPBRec.hFileInfo.ioFVersNum = 0; /* MFS compatibility, see TN #204 */
- myCInfoPBRec.hFileInfo.ioFDirIndex = 0;
-
- retCode = PBGetCatInfoSync(&myCInfoPBRec);
-
- /* set aliasFileFlag if the item is not a directory and the
- aliasFile bit is set */
-
- if (retCode == noErr) {
- /* check directory bit */
- if ((myCInfoPBRec.hFileInfo.ioFlAttrib & ioDirMask) != 0)
- *folderFlag = true;
-
- /* check isAlias bit */
- else if ((myCInfoPBRec.hFileInfo.ioFlFndrInfo.fdFlags & 0x8000) != 0)
- *aliasFileFlag = true;
- }
-
- return retCode;
- }
-
- pascal OSErr ResolveAliasWithMountFlags (const FSSpec * fromFile,
- AliasHandle alias,
- FSSpec * target,
- Boolean * wasChanged,
- unsigned long mountFlags)
- {
- OSErr theErr= noErr;
- Boolean logon = (mountFlags != kResolveAliasFileNoUI);
- Boolean wasAliasedTemp,targetIsFolder;
- *wasChanged = false;
- if ((alias == nil) || (target == nil) || (wasChanged == nil)) return paramErr;
- FSSpec initSpec = *fromFile;
- theErr = FollowFinderAlias (fromFile,
- alias,
- logon,
- target,
- wasChanged);
- if (theErr != noErr) {
- return theErr;
- }
-
- theErr = IsAliasFile(target, &wasAliasedTemp, &targetIsFolder);
- if (theErr != noErr) {
- return theErr;
- }
- if (wasAliasedTemp){
- theErr = ResolveAliasFileWithMountFlags (target,
- true,
- &targetIsFolder,
- &wasAliasedTemp,
- mountFlags) ;
- }
- return theErr;
- }
-